/* * EuroCarbDB, a framework for carbohydrate bioinformatics * * Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * A copy of this license accompanies this distribution in the file LICENSE.txt. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * Last commit: $Rev: 1573 $ by $Author: glycoslave $ on $Date:: 2009-07-24 #$ */ package org.eurocarbdb.action.core; // stdlib imports import java.util.*; import java.math.BigDecimal; // 3rd party imports import org.apache.log4j.Logger; import org.hibernate.Criteria; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.HibernateException; import org.hibernate.ScrollableResults; import org.hibernate.criterion.Order; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.criterion.ProjectionList; import org.hibernate.criterion.Subqueries; // eurocarb imports import org.eurocarbdb.dataaccess.core.*; import org.eurocarbdb.dataaccess.core.seq.*; import org.eurocarbdb.action.EurocarbAction; import org.eurocarbdb.action.BrowseAction; import org.eurocarbdb.dataaccess.EntityManager; import org.eurocarbdb.dataaccess.HibernateEntityManager; import org.eurocarbdb.dataaccess.indexes.*; import org.eurocarbdb.sugar.Sugar; import org.eurocarbdb.sugar.SugarSequence; import org.eurocarbdb.application.glycanbuilder.Glycan; import org.eurocarbdb.dataaccess.SavedGlycanSequenceSearch; // import org.eurocarbdb.dataaccess.core.seq.SavedGlycanSubstructureSearch; // static imports import static org.eurocarbdb.util.StringUtils.join; import static org.eurocarbdb.dataaccess.Eurocarb.getEntityManager; import static org.eurocarbdb.dataaccess.core.seq.SubstructureQuery.Option.*; /* class SearchGlycanSequence *//**************************************** * * Finds a Set of {@link GlycanSequence} objects satisfying a given * set of query predicates. Matched sequences are wrapped together * with their query predicates in a {@link SavedGlycanSequenceSearch} object. * * @see SavedGlycanSequenceSearch * @author mjh * @author hirenj * @version $Rev: 1573 $ */ public class SearchGlycanSequence extends BrowseAction<GlycanSequence> { //~~~~~~~~~~~~~~~~~~~~~~ STATIC FIELDS ~~~~~~~~~~~~~~~~~~~~~~~~~~ /** Logging handle. */ static final Logger log = Logger.getLogger( SearchGlycanSequence.class ); //~~~~~~~~~~~~~~~~~~~~~~~~~~ FIELDS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private String taxonomyName = null; private String tissueName = null; private String diseaseName = null; private String perturbationName = null; private double lowMass = -1, highMass = -1, exactMass = -1, exactMassTolerance = -1; private boolean useAvgMass = false; private boolean useAvgMassGiven = false; private String sequenceGWS; // Position to search for the sequence private String sequencePosition = null; // Stores the input glycan ID that we wish to search for private int glycanId = -1; private boolean validated = false; private boolean isNewQuery = false; private List<SavedGlycanSequenceSearch> queryHistory = new java.util.ArrayList<SavedGlycanSequenceSearch>(); private List<SavedGlycanSequenceSearch> additionalQueries = new java.util.ArrayList<SavedGlycanSequenceSearch>(); private SavedGlycanSequenceSearch currentSearch; private int[] historicalQueriesToRun = {}; private int[] historicalQueriesToRefine = {}; /** The {@link List} of {@link Index}es supported by this Action. */ public static final List<Index<GlycanSequence>> indexes = Arrays.asList( new IndexByContributedDate<GlycanSequence>(), new IndexByContributorName<GlycanSequence>(), new IndexByMostEvidence<GlycanSequence>(), new IndexByResidueCount<GlycanSequence>() ); //~~~~~~~~~~~~~~~~~~~~~~~~~ METHODS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~ criteria creation & access methods ~~~~~ public Criteria createCriteria() { DetachedCriteria crit = createSerializableCriteria(); crit.setProjection( Projections.distinct( Projections.property("glycanSequenceId"))); Criteria criteria = getEntityManager().createQuery(GlycanSequence.class); criteria.add( Subqueries.propertyIn("glycanSequenceId",crit) ); if( getIndex() != null ) { getIndex().apply(criteria); } return criteria; } private DetachedCriteria createSerializableCriteria() { // create base criteria log.debug("creating GlycanSequence criteria"); DetachedCriteria criteria; criteria = DetachedCriteria.forClass( GlycanSequence.class ); // create biological contexts criteria DetachedCriteria bc_criteria = null; DetachedCriteria tax_criteria = null; DetachedCriteria tissue_criteria = null; DetachedCriteria disease_criteria = null; DetachedCriteria perturbation_criteria = null; if( taxonomyName!=null || tissueName!=null || diseaseName!=null || perturbationName != null ) { isNewQuery = true; log.debug("creating Biological context criteria"); bc_criteria = criteria.createCriteria("glycanContexts") .createCriteria("biologicalContext", "bc"); // add taxonomy criteria if( taxonomyName!=null ) { log.debug("adding taxonomy query predicates for input string '" + taxonomyName + "'"); tax_criteria = bc_criteria.createCriteria("taxonomy", "taxa") .createCriteria("taxonomySupertypes", "supertax") .add( Restrictions.ilike( "taxon", taxonomyName, MatchMode.EXACT ) ); } // add tissue criteria if( tissueName!=null ) { log.debug("adding tissue query predicates for input string '" + tissueName + "'"); tissue_criteria = bc_criteria.createCriteria("tissueTaxonomy", "ttax") .add( Restrictions.ilike("tissueTaxon", tissueName, MatchMode.EXACT ) ); } // add disease criteria if( diseaseName!=null ) { log.debug("adding disease query criteria for input string '" + diseaseName + "'"); disease_criteria = bc_criteria.createCriteria("diseaseContexts") .createCriteria("disease", "dis") .add( Restrictions.ilike("diseaseName", diseaseName, MatchMode.EXACT ) ); } if ( perturbationName!=null ) { log.debug("adding perturbation query criteria for input string '" + perturbationName + "'"); perturbation_criteria = bc_criteria.createCriteria("perturbationContexts") .createCriteria("perturbation", "per") .add( Restrictions.ilike("perturbationName", perturbationName, MatchMode.EXACT ) ); } } // add mass criteria boolean mass_query_is_given = false; boolean params_are_ok = false; if ( exactMass > 0 && exactMassTolerance > 0 ) { isNewQuery = true; mass_query_is_given = true; lowMass = exactMass - exactMassTolerance; highMass = exactMass + exactMassTolerance; log.debug( "adding predicates for exactMass=" + exactMass + " Da +/- " + exactMassTolerance + " Da (ie: " + lowMass + "-" + highMass + " Da)" ); params_are_ok = true; } else if ( lowMass > 0 && highMass > 0 ) { isNewQuery = true; mass_query_is_given = true; exactMass = -1; exactMassTolerance = -1; log.debug( "adding predicates for mass range=(" + lowMass + ".." + highMass + " Da)" ); params_are_ok = true; } if ( mass_query_is_given ) { if ( params_are_ok ) { isNewQuery = true; String property = useAvgMass ? "massAverage" : "massMonoisotopic"; criteria.add( Restrictions.between(property, new BigDecimal(lowMass), new BigDecimal(highMass) ) ); } else { String msg = "Insufficient mass parameters given, either " + "provide an exactMass + exactMassTolerence + useAvgMass preference, " + "or provide a lowMass + highMass + useAvgMass preference"; addActionError( msg ); log.info( msg ); } } Glycan glycan = null; if ( sequenceGWS != null ) { glycan = Glycan.fromString(sequenceGWS); glycan.removeReducingEndModification(); if (glycan.isEmpty()) { glycan = null; sequenceGWS = null; } } if ( glycan != null ) { isNewQuery = true; // search structure in DB String glycoct = glycan.toGlycoCTCondensed(); SugarSequence seq = new SugarSequence( glycoct ); SubstructureQuery query = new SubstructureQuery( seq ); if ( sequencePosition != null ) { if ( sequencePosition.equals("Core") || sequencePosition.equals("Core + Terminii") ) query.setOption( Must_Include_Reducing_Terminus ); if ( sequencePosition.equals("Terminii") || sequencePosition.equals("Core + Terminii") ) query.setOption( Must_Include_All_Non_Reducing_Terminii ); } criteria.add( query.getQueryCriterion() ); } if ( this.additionalQueries.size() > 1 ) { isNewQuery = true; } for ( SavedGlycanSequenceSearch oldQuery : this.additionalQueries ) { DetachedCriteria oldCriteria = oldQuery.getQueryCriteria(); criteria.add( Subqueries.propertyIn("glycanSequenceId", oldCriteria) ); oldCriteria.setProjection( Projections.distinct( Projections.property("glycanSequenceId"))); this.currentSearch = oldQuery; } return criteria; } //~~~~~~~~~~~~ Query history methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~ public List<SavedGlycanSequenceSearch> getQueryHistory() { return queryHistory; } public void setQueryHistory(List<SavedGlycanSequenceSearch> history) { this.queryHistory = history; } public int[] getHistoricalQueriesToRefine() { return this.historicalQueriesToRefine; } public void setHistoricalQueriesToRefine( int[] ids ) { this.historicalQueriesToRefine = ids; for ( int index : ids ) { if (this.getQueryHistory().size() > index) { this.additionalQueries.add(this.getQueryHistory().get(index)); } } } public int[] getHistoricalQueriesToRun() { return this.historicalQueriesToRun; } public void setHistoricalQueriesToRun( int[] ids ) { this.historicalQueriesToRun = ids; for( int index : ids ) { if ( this.getQueryHistory().size() > index ) { this.additionalQueries.add( this.getQueryHistory().get(index) ); } } } public List<SavedGlycanSequenceSearch> getAdditionalQueries() { return this.additionalQueries; } public SavedGlycanSequenceSearch getCurrentSearch() { return this.currentSearch; } //~~~~~~~~~~~~ query predicate creation methods ~~~~~~~~~~~~~~~ public String getSequenceGWS() { return sequenceGWS; } public void setSequenceGWS(String str) { sequenceGWS = str; } public boolean isSearchCore() { return false; } public boolean isSearchTerminal() { return false; } // taxonomy query predicates // public void setTaxonomyName( String namestring ) { if( namestring!=null && namestring.trim().length()>0 ) taxonomyName = namestring.trim(); else taxonomyName = null; } public String getTaxonomyName() { return taxonomyName; } public void setTissueName( String namestring ) { if( namestring!=null && namestring.trim().length()>0 ) tissueName = namestring.trim(); else tissueName = null; } public String getTissueName() { return tissueName; } public void setDiseaseName( String namestring ) { if( namestring!=null && namestring.trim().length()>0 ) diseaseName = namestring.trim(); else diseaseName = null; } public String getDiseaseName() { return diseaseName; } /** * Get accessor for perturbationName * Perturbation query string accessor */ public String getPerturbationName() { return this.perturbationName; } /** * Set accessor for perturbationName * @param perturbationName Data to set * Perturbation query string accessor */ public void setPerturbationName(String namestring) { if( namestring!=null && namestring.trim().length()>0 ) perturbationName = namestring.trim(); else perturbationName = null; } // mass query predicates // public void setAvgMass( boolean b ) { useAvgMass = b; useAvgMassGiven = true; } public boolean getAvgMass() { return useAvgMass; } public void setMonoisoMass( boolean b ) { useAvgMass = ! b; useAvgMassGiven = true; } public void setDiscreteMass( double mass ) { exactMass = mass; } public double getDiscreteMass() { return exactMass; } public void setDiscreteMassTolerance( double tolerance ) { exactMassTolerance = Math.abs( tolerance ); } public double getDiscreteMassTolerance() { return exactMassTolerance; } public void setLowMass( double mass ) { lowMass = mass; } public double getLowMass() { return lowMass; } public void setHighMass( double mass ) { highMass = mass; } public double getHighMass() { return highMass; } /** * Get accessor for sequencePosition * Position to search for the sequence */ public String getSequencePosition() { return this.sequencePosition; } /** * Set accessor for sequencePosition * @param sequencePosition Data to set * Position to search for the sequence */ public void setSequencePosition(String sequencePosition) { this.sequencePosition = sequencePosition; } /** * Get accessor for glycanId * Stores the input glycan ID that we wish to search for */ public int getGlycanId() { return this.glycanId; } /** * Set accessor for glycanId * @param glycanId Data to set * Stores the input glycan ID that we wish to search for */ public void setGlycanId(int glycanId) { this.glycanId = glycanId; } public String[] getQueryDescription() { ArrayList<String> descriptions = new ArrayList<String>(); if ( getTaxonomyName() != null ) { descriptions.add( "taxonomy equals " + getTaxonomyName() ); } if ( getTissueName() != null ) { descriptions.add( "tissue equals " + getTissueName() ); } if ( getDiseaseName() != null ) { descriptions.add( "disease equals " + getDiseaseName() ); } if ( getPerturbationName() != null ) { descriptions.add( "perturbation equals " + getPerturbationName() ); } if ( exactMass > 0 || lowMass > 0 ) { String massType = useAvgMass ? "average" : "monoisotopic"; if ( exactMass > 0 ) { descriptions.add( massType + " mass equals " + exactMass + " ± " + exactMassTolerance + " Da" ); } else { descriptions.add( massType + " between " + lowMass + " and " + highMass + " Da" ); } } if ( getSequenceGWS() != null ) { descriptions.add( " substructure is found" + ((sequencePosition != null && ! "Anywhere".equals( sequencePosition ) ) ? " at " + sequencePosition.toLowerCase() : "" ) ); } for ( SavedGlycanSequenceSearch query : this.additionalQueries ) { if ( query.description != null ) { descriptions.add( query.description ); } } return descriptions.toArray( new String[0] ); } //~~~~~~~~~~~~~~~~~ composition query options ~~~~~~~~~~~~~~~~~~~ public void setExactComp( String s ) { /* TODO */ } public void setMinComp( String s ) { /* TODO */ } public void setMaxComp( String s ) { /* TODO */ } //~~~~~~~~~~~~~~~~~~~~~~~~ indexing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** Default index is the first index in the list of indexes */ @Override public Index<GlycanSequence> getDefaultIndex() { return indexes.get( 0 ); } @Override public List<Index<GlycanSequence>> getIndexes() { return indexes; } public final Class<GlycanSequence> getIndexableType() { return GlycanSequence.class; } //~~~~~~~~~~~~~~~~~~~~~~~ validation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void validate() { // return unless we're processing input params if ( getParameters() == null || getParameters().size() == 0 ) return; // only perform validation once, per request if ( validated ) return; validated = true; } public List<GlycanSequence> getQueryResults() { Criteria query = createCriteria(); log.info( "Performing query: " + query.toString() ); setMessage(query.toString()); ScrollableResults scroll = null; try { scroll = query.scroll(); scroll.last(); setTotalResults(scroll.getRowNumber()+1); int count = getTotalResults(); int first = getOffset(); int max = getMaxResults(); if ( first > 0 ) { query.setFirstResult(first); } if ( max > 0 ) { query.setMaxResults(max); } List<GlycanSequence> ret = (List<GlycanSequence>) query.list(); log.debug( "query executed ok, results count=" + ret.size() ); return ret; } catch ( HibernateException e ) { log.warn( "Caught " + e.getClass().getName() + " performing query:", e ); return Collections.emptyList(); } finally { if ( scroll != null ) scroll.close(); } } /* execute *///************************************************ @SuppressWarnings("unchecked") public String execute() { if ( getParameters() == null || getParameters().size() == 0 ) { log.debug("no input params given, returning 'input' view"); return "input"; } if ( getParameters().get("historicalQueriesToRefine") != null) { log.debug("refining an existing query, returning 'input' view"); return "input"; } if ( glycanId > 0 ) { return "show"; } validate(); setResults( getQueryResults() ); if ( isNewQuery ) { SavedGlycanSequenceSearch savedSearch = new SavedGlycanSequenceSearch(); savedSearch.queryCriteria = createSerializableCriteria(); savedSearch.description = join(" AND ", getQueryDescription()); savedSearch.resultCount = getTotalResults(); savedSearch.sequence = sequenceGWS; List<SavedGlycanSequenceSearch> history = this.getQueryHistory(); history.add(savedSearch); this.setQueryHistory(history); this.currentSearch = savedSearch; } return hasActionErrors() ? "input" : "success"; } } // end class